/*
 * @Author: Suo-Yuheng syh15399227325@163.com
 * @Date: 2025-09-23 10:54:46
 * @LastEditors: Suo-Yuheng syh15399227325@163.com
 * @LastEditTime: 2025-09-23 17:46:05
 * @FilePath: /0923/EpollServer.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 *
 *
 */
#pragma once

#include <iostream>
#include <string>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "nocopy.hpp"

uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);

class EpollServer : public nocopy
{
    static const int num = 64;

public:
    EpollServer(uint16_t port)
        : _port(port), _listensock_ptr(new Sock()), _epoller_ptr(new Epoller())
    {
    }

    void Init()
    {
        _listensock_ptr->Socket();
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();

        lg(Info, "create listen socket success: %d\n", _listensock_ptr->Fd());
    }

    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sock = _listensock_ptr->Accept(&clientip, &clientport); // 这里完全不会被阻塞，因为已经就绪了
        if (sock > 0)
        {
            // 这里不能直接 read 读，不一定写了数据，如果没写 就读，那就阻塞到这里了
            // 应该 把新获取的连接，交给 epoll 去监听事件
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, EVENT_IN);
            lg(Info, "get a new link, client info@ %s:%d", clientip.c_str(), clientport);
        }
    }

    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug? 如果没读到一个完整的请求呢？
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "get a message: " << buffer << std::endl;

            // 服务器写回给client
            std::string echo_str = "server echo $ " + std::string(buffer);
            write(fd, echo_str.c_str(), echo_str.size());   // bug？
        }
        else if (n == 0)
        {
            lg(Info, "client quit, me too, close fd is: %d", fd);
            // 对方已经断开了，需要从 epoll 中把 对应的 fd和事件 移除
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd); // 必须先 移除，再关，因为 移除时，必须保证 fd 合法
        }
        else
        {
            lg(Warning, "recv error, fd is: %d", fd);
            // 对方已经断开了，需要从 epoll 中把 对应的 fd和事件 移除
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd); // 必须先 移除，再关，因为 移除时，必须保证 fd 合法
        }
    }

    void Dispatcher(struct epoll_event revs[], int num)
    {
        // 处理 已经就绪的 fd 及其 关心的事件
        for (int i = 0; i < num; i++)
        {
            uint32_t events = revs[i].events;
            int fd = revs[i].data.fd;

            if (events & EVENT_IN) // 读事件就绪
            {
                if (fd == _listensock_ptr->Fd()) // 如果是个监听套接字，这是个 新连接
                {
                    Accepter(); // 连接管理器
                }
                else // fd 上的普通 读事件就绪
                {
                    Recver(fd); // 事件处理器
                }
            }
            else if (events & EVENT_OUT) // 写事件就绪
            {
            }
            else
            {
            }
        }
    }

    void Start()
    {
        // 将 最开始只有的 listensock 添加到 epoll 中：就是把 listensock 和它关心的事件，添加到内核 epoll 模型中，rb_tree
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, _listensock_ptr->Fd(), EVENT_IN);

        struct epoll_event revs[num];
        for (;;)
        {
            int n = _epoller_ptr->EpollerWait(revs, num); // 每次从底层 捞 num 个就绪的事件，到输出型参数 revs 中；
                                                          // 如果一次捞满了，直接返回，剩下的下次再捞

            if (n > 0)
            {
                // 有事件就绪了
                lg(Debug, "event happend, fd is: %d\n", revs[0].data.fd);

                // 处理事件
                Dispatcher(revs, n);
            }
            else if (n == 0)
            {
                lg(Info, "time out...");
            }
            else
            {
                lg(Error, "epoll wait error");
            }
        }
    }

    ~EpollServer()
    {
        _listensock_ptr->Close();
    }

private:
    std::shared_ptr<Sock> _listensock_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;
    // Sock _listensock;
    // Epoller _epoller;
    uint16_t _port;
};